#include "PPoller.h"
#include "Channel.h"
#include "Eventloop.h"
#include "log/Logger.h"
#include "utility/assertion.h"

#include <poll.h>

PPoller::PPoller(Eventloop *loop):
    Poller{loop},
    _pollfds{},
    _channels{}
{
    
}

PPoller::~PPoller() = default;


Utility::timepoint PPoller::poll(int timeout, ChannelList *activeChannels) {
    int numEvents = ::poll(_pollfds.data(), _pollfds.size(), timeout);
    auto now = Utility::now();
    if (numEvents > 0) {
        _fillActiveChannels(numEvents, activeChannels);
    }
    else if (numEvents == 0) {
        LOG_VERBOSE << "no events found";
    } 
    else {
        LOG_SYSERROR << "PPoller::poll()";
    }
    return now;
}


void PPoller::_fillActiveChannels(int numEvents, ChannelList *activeChannels) const {
    for (auto it = _pollfds.begin(); it != _pollfds.end() && numEvents > 0; ++it) {
        if (it->fd > 0) {
            --numEvents;
            auto ch = _channels.find(it->fd);
            assert(ch != _channels.cend());
            auto channel = ch->second;
            assert(channel->fd() == it->fd);
            channel->set_revents(it->revents);
            activeChannels->push_back(channel);
        }
    }
}


void PPoller::update_channel(Channel *channel)
{
    Poller::assert_in_loopthread();
    LOG_VERBOSE << "fd = " << channel->fd() << ", events = " << channel->events();
    if (channel->index() < 0) {
        // a new channel if _index < 0
        assert(_channels.find(channel->fd()) == _channels.cend());
        struct pollfd pfd;
        pfd.fd = channel->fd();
        pfd.events = static_cast<short>(channel->events());
        pfd.revents = 0;
        _pollfds.push_back(pfd);
        int idx = static_cast<int>(_pollfds.size() - 1);
        channel->set_index(idx);
        _channels[pfd.fd] = channel;
    }
    else {
        assert(_channels.find(channel->fd()) != _channels.cend());
        assert(_channels[channel->fd()]  == channel);
        int idx = channel->index();
        assert(Cast::LT(0, idx) && Cast::LT(idx, _pollfds.size()));
        auto &pfd = _pollfds[idx];
        assert(pfd.fd = channel->fd() || pfd.fd == -channel->fd() - 1);
        pfd.events = static_cast<decltype(pfd.events)>(channel->events());
        pfd.revents = 0;
        if (channel->is_non_event()){
            pfd.fd = -channel->fd() - 1;
        }
    }
}

void PPoller::remove_channel(Channel *channel)
{
    Poller::assert_in_loopthread();
    LOG_VERBOSE << "fd = " << channel->fd();
    assert(_channels.find(channel->fd()) != _channels.end());
    assert(channel->is_non_event());
    auto idx = channel->index();
    assert(Cast::LT(0, idx) && Cast::LT(idx, _pollfds.size()));
    auto &pfd = _pollfds[idx]; (void)pfd;
    assert(pfd.fd == channel->fd() && pfd.fd == -channel->fd() - 1);
    auto n = _channels.erase(channel->fd()); (void)n;
    assert(n == 1);

    if ( Cast::EQ(idx,_pollfds.size() - 1)) {
        // if the fd is the last one, just pop it
        _pollfds.pop_back();
    }
    else {
        // swap the deleted fd with last one, so as to reduce data movement
        int pollfdAtEnd = _pollfds.back().fd;
        std::iter_swap(_pollfds.begin() + idx, _pollfds.end() - 1);
        // get real fd for negative fd
        if (pollfdAtEnd < 0) {
            pollfdAtEnd = -pollfdAtEnd - 1;
        }
        _channels[pollfdAtEnd]->set_index(idx);
        _pollfds.pop_back();
    }

}
